package cn.haoma.base.socket;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

import cn.haoma.base.socket.inf.Messenger;
import cn.haoma.base.socket.inf.MsgCallBack;
import cn.haoma.base.socket.inf.MsgListener;
import cn.haoma.base.socket.inf.SocketHandler;
import cn.haoma.base.utils.L;
import cn.haoma.base.utils.NullUtil;
import cn.haoma.base.network.ConfigConst;
import cn.haoma.base.socket.inf.ConnectionStatus;
import cn.haoma.base.socket.inf.ConnectionStatusListener;

public class MessengerFactory implements Messenger, Runnable {

    public static final int HEAT_BEAT_INTERVAL = 10000;
    private static final String TAG = ConfigConst.LOG_TAG_SOCKET;
    private static final String TAG_MSG = ConfigConst.LOG_TAG_SOCKET + "_MSG";
    private static final String TAG_SEND = TAG_MSG + "_SEND";
    private static final String TAG_RECEIVE = TAG_MSG + "_RECEIVE";
    private final static byte[] EMPTY_DATA = new byte[0];
    private static Messenger instance;
    private static Context context;
    private String host;
    private int port;
    private DataInputStream dataInputStream;
    private DataOutputStream dataOutputStream;
    private Socket socket;
    private SocketHandler socketHandler;
    private InputStream socketInputScreem;
    private ConnectionStatus status = ConnectionStatus.STATUS_DISCONNECTED;
    private Msg heatBeatMsg;
    private boolean autoConnect;
    private Handler heartBeatHandler = new Handler();
    private Handler reconnectHandler = new Handler();
    private Runnable reconnectRunnable = new Runnable() {
        @Override
        public void run() {
            autoReconnectSocket();
        }
    };
    private boolean isAutoReconnecting;
    private long lastMsgReceiveTime;
    private Runnable sendCmdRunnable = new Runnable() {
        @Override
        public void run() {
            sendMessage(heatBeatMsg);
        }
    };

    private MessengerFactory() {
        heatBeatMsg = new Msg();
        heatBeatMsg.cmd = MsgProtocol.CMD_HEART_BEAT;
    }

    public static Messenger getInstance() {
        if (null == instance) {
            instance = new MessengerFactory();
        }
        return instance;
    }

    /**
     * @return 网络是否是联通状态
     */
    public static boolean isNetConnected() {
        boolean isConnect = false;
        ConnectivityManager manager = (ConnectivityManager) context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (manager != null) {
            NetworkInfo networkinfo = manager.getActiveNetworkInfo();
            if (networkinfo != null && networkinfo.isAvailable()) {
                isConnect = true;
            }
        }
        L.i(ConfigConst.LOG_TAG_SOCKET, "检查网络" + (isConnect ? "已连接" : "断开"));
        return isConnect;
    }

    /**
     * 重新连接
     */
    private void autoReconnectSocket() {
        if (status != ConnectionStatus.STATUS_DISCONNECTED) {
            return;
        }
        if (autoConnect) {
            reconnectHandler.removeCallbacks(reconnectRunnable);
            if (isNetConnected()) {
                reconnectHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        L.i(TAG, "自动重连Socket， initSocket");
                        initSocket(context);
                    }
                }, 5000);//10秒后检查网络状况
            } else {
                L.i(TAG, "10秒后自动重连Socket， initSocket");
                reconnectHandler.postDelayed(reconnectRunnable, 10000);//10秒后检查网络状况
            }
        }
    }

    private void closeOldSocketIfNecessary() {
        if (null != socket && !socket.isClosed()) {
            try {
                L.i(TAG, "关闭原来的SOCKET");
                socketInputScreem = null;
                dataInputStream = null;
                dataOutputStream = null;
                socket.close();
                socket = null;
            } catch (Exception e) {
                L.e(e);
            }
        }
    }

    @Override
    public void closeSocket() {
        if (status != ConnectionStatus.STATUS_DISCONNECTED) {
            status = ConnectionStatus.STATUS_DISCONNECTED;
            try {
                autoConnect = false;
                socketInputScreem = null;
                dataInputStream = null;
                dataOutputStream = null;
                if (null != socket && socket.isConnected()) {
                    socket.close();
                    socket = null;
                    if (null != socketHandler) {
                        socketHandler.onSocketClosed();
                    }
                    if (null != heartBeatHandler) {
                        heartBeatHandler.removeCallbacksAndMessages(heatBeatMsg);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    public ConnectionStatus getStatus() {
        return status;
    }

    @Override
    public void initSocket(Context c) {
        if (ConnectionStatus.STATUS_DISCONNECTED == status) {
            L.i(TAG, "初始化SOCKET");
            closeOldSocketIfNecessary();
            context = c;
            status = ConnectionStatus.STATUS_CONNECTING;
            Thread thread = new Thread(this);
            thread.setName(getClass().getSimpleName());
            thread.start();
        }
    }

    @Override
    public void registMsgListener(MsgListener listener) {
        socketHandler.onRegistMsgListener(listener);
    }

    @Override
    public void registProcesser(SocketHandler handler) {
        socketHandler = handler;
    }

    @Override
    public void registStatusListener(ConnectionStatusListener listener) {
        socketHandler.onRegistStatusListener(listener);
    }

    @Override
    public void sendMessage(Msg msg) {
        sendMessage(msg, null);
    }

    @Override
    public void sendMessage(Msg msg, MsgCallBack msgCallBack) {
        if (status == ConnectionStatus.STATUS_CONNECTED) {
            try {
                dataOutputStream.writeByte(msg.cmd);
                if (msg.needConfirm) {
                    dataOutputStream.writeByte(1);//消息需要确认
                } else {
                    dataOutputStream.writeByte(0);//消息无需确认
                }
                dataOutputStream.writeInt(msg.id);//消息ID

                byte[] data = EMPTY_DATA;
                if (!NullUtil.em(msg.content)) {
                    data = msg.content.getBytes(MsgProtocol.CHARSET);
                    EncryptUtil.encrypt(data);
                }
                dataOutputStream.writeInt(data.length);
                dataOutputStream.write(data);
                dataOutputStream.flush();
                socketHandler.onSendMsg(msg, msgCallBack);
                sendDelayedHeartBeatCmd();
                L.saveToLog("Send:" + msg.toString());
                L.i(TAG_SEND, msg.toString());
            } catch (IOException e) {
                onDisconnected();
            }
        } else {
            if (null != msgCallBack) {
                msgCallBack.onMsgFailToSend(msg);
            }
        }
    }

    @Override
    public void setAddress(String host, int port) {
        this.host = host;
        this.port = port;
    }

    @Override
    public void setAutoReconnect(boolean autoReconnect) {
        this.autoConnect = autoReconnect;
    }

    @Override
    public void unregistMsgListener(MsgListener listener) {
        socketHandler.onUnregistMsgListener(listener);
    }

    @Override
    public void unregistStatusListener(ConnectionStatusListener listener) {
        socketHandler.onUnregistStatusListener(listener);
    }    @Override
    public void run() {
        try {
            try {
                socket = new Socket(host, port);
            } catch (Exception e) {
                L.i(TAG, "SOCKET 初始化失败");
                onDisconnected();
                return;
            }
            socket.setKeepAlive(true);
            socket.setSoTimeout(HEAT_BEAT_INTERVAL);

            status = ConnectionStatus.STATUS_CONNECTED;
            socketHandler.onSocketConnected();

            socketInputScreem = socket.getInputStream();
            dataInputStream = new DataInputStream(socket.getInputStream());
            dataOutputStream = new DataOutputStream(socket.getOutputStream());
            lastMsgReceiveTime = System.currentTimeMillis();
            sendMessage(heatBeatMsg);
            processInputStream();
        } catch (Exception e) {
            L.e(TAG, e);
        } catch (Error e) {
            L.e(TAG, e);
        } finally {
            onDisconnected();
        }
    }

    private void onDisconnected() {
        if (status == ConnectionStatus.STATUS_DISCONNECTED) {
            return;
        }
        status = ConnectionStatus.STATUS_DISCONNECTED;
        if (null != socket && !socket.isClosed()) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (null != socketHandler) {
            socketHandler.onSocketClosed();
        }
        if (null != heartBeatHandler) {
            heartBeatHandler.removeCallbacksAndMessages(heatBeatMsg);
        }
        if (autoConnect) {
            autoReconnectSocket();
        }
    }

    /**
     * 分包，解码
     *
     * @throws IOException
     */
    private void processInputStream() throws Exception {
        try {
            while (null != socket && socket.isConnected()) {
                if (socketInputScreem.available() < 10) {
                    Thread.sleep(100);
                    continue;
                }
                int totalLength = 0;
                Msg msg = new Msg();
                msg.cmd = dataInputStream.readByte();
                msg.needConfirm = (1 == dataInputStream.readByte());
                msg.id = dataInputStream.readInt();
                totalLength = dataInputStream.readInt();
                if (totalLength > 1024 * 1024 * 2) {//文本超过2M视为数据异常
                    break;
                }
                byte[] data = new byte[totalLength];
                int start = 0;
                while (null != socket && socket.isConnected()) {
                    int n = socketInputScreem.read(data, start, totalLength - start);
                    start += n;
                    if (start == totalLength) {
                        EncryptUtil.encrypt(data);
                        String msgContent = new String(data, MsgProtocol.CHARSET);
                        msg.content = msgContent;
                        L.i(TAG_RECEIVE, msg.toString());
                        if (msg.cmd != MsgProtocol.CMD_HEART_BEAT) {
                            socketHandler.onMsgReceived(msg);
                        }
                        break;
                    }
                }
                lastMsgReceiveTime = System.currentTimeMillis();
                L.saveToLog("Receive:" + msg.toString());
                sendDelayedHeartBeatCmd();
            }
        } catch (Error e) {
            System.gc();
            throw new Exception();
        }
    }

    /**
     * 撤销之前未发出的定时消息，重新发一个5秒后的定时消息
     */
    private void sendDelayedHeartBeatCmd() {
        if (System.currentTimeMillis() - lastMsgReceiveTime > 2 * HEAT_BEAT_INTERVAL) {//
            L.i(TAG, "心跳超时");
            onDisconnected();
        } else {
            heartBeatHandler.removeCallbacks(sendCmdRunnable);
            heartBeatHandler.postDelayed(sendCmdRunnable, HEAT_BEAT_INTERVAL);
        }
    }




}
